#ifndef BACKPACK_ITEM_H_
#define BACKPACK_ITEM_H_
#pragma once

#include "ItemVariant.hpp"

#include <string>
#include <boost/utility.hpp>
#include <functional>

namespace backpack {





/**
 * /class Item base clas of all items
 *
 * All item types must inherit from this class.
 */
template <class Derived>
class Item : boost::noncopyable {
public:
  std::string const& getName() const {
    return m_strName;
  }
  std::string const& getDescription() const {
    return m_strDescription;
  }

  bool operator<(Item const& rhs) const {
    return m_strName < rhs.m_strName;
  }
  bool operator==(Item const& rhs) const {
    return m_strName == rhs.m_strName;
  }
  bool operator!=(Item const& rhs) const {
    return m_strName != rhs.m_strName;
  }
  operator const std::reference_wrapper<const Derived>() const {
    return std::cref(*static_cast<const Derived* const>(this));
  }

  operator std::reference_wrapper<const Derived>() {
    return std::cref(*static_cast<Derived*>(this));
  }
protected:
  Item(std::string const& name, std::string const& description)
    : m_strName(name), m_strDescription(description) {
  }

private:
  std::string const m_strName;
  std::string const m_strDescription;
}; // class Item



class Key : public Item<Key> {
public:
  const static Key c_null;

  typedef Item<Key> Base;
  Key(std::string const& name, std::string const& description)
    : Base(name, description) {
  }
}; // class Key



class StoryItem : public Item<StoryItem> {
public:
  typedef Item<StoryItem> Base;
  StoryItem(std::string const& name, std::string const& description,
    std::string const& story)
    : Base(name, description), m_story(story) {
  }
  std::string const& getStory() const {
    return m_story;
  }
private:
  std::string const m_story;
}; // class StoryItem



class ItemPart : public Item<ItemPart> {
public:
  template <class ItemPartIterator>
  static ItemVariant tryCombine(ItemPartIterator begin, ItemPartIterator end);

  struct Combination {
    Combination(ItemVariant const& product = g_itemNull, int nComponents = 0)
      : m_rProduct(product), m_nComponents(nComponents) {
    }
    ItemVariant const& m_rProduct;
    int const m_nComponents;
  }; // struct Combination

  typedef Item<ItemPart> Base;
  ItemPart(std::string const& name, std::string const& description)
    : Base(name, description) {
  }
  void setCombination(Combination *combination) {
    m_combination = combination;
  }

private:
  Combination *m_combination;
}; // class ItemPart




template <class ItemPartIterator>
ItemVariant ItemPart::tryCombine(ItemPartIterator begin, ItemPartIterator end) {
  if (begin - end < 2) {
    throw std::exception("at least two item parts needed to tryCombine");
  }

  ItemPart const& part = *begin;
  Combination const& combination = *part.m_combination;
  if (end - begin != combination.m_nComponents) {
    return NULL;
  }
  for (ItemPartIterator it = begin+1; it!=end; ++it) {
    if (*(it->m_combination) != combination) {
      return NULL;
    }
  }

  return combination.m_rProduct;
}



struct NullItem : public Item<NullItem> {
  NullItem() : Item<NullItem>("NULL Item", "") {
  }
}; // struct NullItem



} // namespace backpack

#endif // BACKPACK_ITEM_H_

